package towerDefense;

import java.awt.geom.Rectangle2D;

/**
 * The super-class of all objects in the game. It provides x and y coordinates 
 * for the object in the game world. It also laysout any information needed by 
 * all classes, this includes but is not limited to Object location, size in 
 * the game world, object type, and object owner (Player or Computer). 
 */
public class T_Object {
	//PUBLIC VARIABLES
	/**
	 * Default size of all objects is set to 10. This is the size of all towers. 
	 */
	public static final int DEFAULT_SIZE = 10;
	
	/**
	 * Defines the bounding box of the object simply. No need to maintain a record
	 * of the specific size or location when a rectangle can maintain the information
	 * easily.  
	 */
	private Rectangle2D.Double SPACE;

	/**
	 * What type of Object this is. The list of what type of objects are available 
	 * are listed below. <ul>
	 * <li><b>0</b> -- Tower Bullet</li>
	 * <li><b>1</b> -- Tower Fire</li>
	 * <li><b>2</b> -- Tower </li>
	 * <li><b>3</b> -- Tower </li>
	 * <li><b>4</b> -- Projectile Bullet</li>
	 * <li><b>5</b> -- Projectile Fire</li>
	 * <li><b>6</b> -- Projectile </li>
	 * <li><b>7</b> -- Projectile </li>
	 * <li><b>8</b> -- Enemy </li>
	 * <li><b>9</b> -- Enemy </li>
	 * <li><b>10</b> -- Enemy </li>
	 * <li><b>11</b> -- Enemy </li>
	 * </ul> 
	 */
	private static int TYPE;

	/**
	 * The player that owns this object
	 */
	private static Player PLAYER;

	/**
	 * Places an object in the 0,0 coordinate in the map. If the 
	 * the coordinate is already taken it will try and place the 
	 * object in a different location. 
	 */
	public T_Object(int type){
		TYPE = type;
		if (moveObject(0, 0, DEFAULT_SIZE)) 
			World.addObject(this, TYPE);
		else 
			System.out.println("Cannot place object");
	}

	/**
	 * Places an object in the {@code x_coord},{@code y} 
	 * coordinate in the map. If the coordinate is already taken 
	 * it will try and place the object in a different location.
	 * 
	 * @param x_coord The center X Coordinate the Object. 
	 * @param y_coord The center Y Coordinate the Object.
	 * @param size The size of the object (height/width). 
	 */
	public T_Object(double x_coord, double y_coord, double size, int type){
		TYPE = type;
		if (moveObject(x_coord, y_coord, size)) 
			World.addObject(this, TYPE);
		else 
			System.out.println("Cannot place object");
	}

	/**
	 * Return the numerical type of the object. 
	 * @return The numerical type of the object. 
	 */
	public int getType(){
		return TYPE;
	}
	
	/**
	 * Returns the Player that owns the object. 
	 * @return The {@link towerDefense.Player} that owns the object. 
	 */
	public Player getPlayer(){
		return PLAYER;
	}
	
	/**
	 * Return the center X coordinate position of this object.
	 * 
	 * @return The center x coordinate position
	 */
	public double getX() {
		return SPACE.getCenterX();
	}

	/**
	 * Return the center Y coordinate position of this object.
	 * 
	 * @return The center y coordinate position
	 */
	public double getY() {

		return SPACE.getCenterY();
	}
	
	/**
	 * Returns the bounding box that describes the object. 
	 * 
	 * @return The bounding box that describes the object.  
	 */
	public Rectangle2D.Double getSpace(){
		return SPACE;
	}

	/**
	 * Returns the distance in units to the given object.
	 * 
	 * @param obj an object
	 * @return the distance to the position in units
	 */
	public double getDistanceTo(T_Object obj) {
		return Math.sqrt(((SPACE.getCenterX()-obj.SPACE.getCenterX())*(SPACE.getCenterX()-obj.SPACE.getCenterX()))+
				((SPACE.getCenterY()-obj.SPACE.getCenterY())*(SPACE.getCenterY()-obj.SPACE.getCenterY())));
	}

	/**
	 * Returns the distance in units to the given position.
	 * 
	 * @param x_coord an x_coord position
	 * @param y_coord a y position
	 * @return the distance to the position in units
	 */
	public double getDistanceTo(double x_coord, double y_coord) {
		return Math.sqrt(((SPACE.getCenterX()-x_coord)*(SPACE.getCenterX()-x_coord))+
				((SPACE.getCenterY()-y_coord)*(SPACE.getCenterY()-y_coord)));
	}

	/**
	 * Returns the absolute heading (angle) to the given object.
	 * 
	 * Pi/2 is due north. 0 and 2Pi is due east. Pi is due west. 
	 * 3Pi/2 is due south  
	 * 
	 * @param obj an object
	 * @return the heading (angle) in radians
	 */
	public double getHeadingTo(T_Object obj) {
		return Math.atan((SPACE.getCenterX()-obj.SPACE.getCenterX())/(SPACE.getCenterY()-obj.SPACE.getCenterY()));
	}

	/**
	 * Returns the absolute heading (angle) to the given position.
	 * 
	 * @param x_coord an x position
	 * @param y_coord a y position
	 * @return the heading (angle) in radians
	 */
	public double getHeadingTo(double x_coord, double y_coord) {
		return Math.atan((SPACE.getCenterX()-x_coord)/(SPACE.getCenterY()-y_coord));
	}

	/**
	 * Move/Place the object on the given position. Can only be moved in the positive x,y plane. 
	 * 
	 * @param x_coord The center X Coordinate the Object will be moved to. 
	 * @param y_coord The Y Coordinate the Object will be moved to. 
	 * @param _size Size of the new Object. 
	 * @return {@code true} if the move was successful, {@code false} 
	 * otherwise. 
	 */
	public boolean moveObject(double x_coord, double y_coord, double _size) {

		boolean unoccupied=true;
		T_Object[] worldObjects = World.getObjects();
		// ensures all variables are a positive value
		double upperLeftX = Math.abs(x_coord), upperLeftY=Math.abs(y_coord), size = Math.abs(_size);

		//check to make sure  there isn't an object currently in the location, if found set to false
		for (T_Object worldObject : worldObjects){
			if (worldObject.SPACE.intersects(upperLeftX, upperLeftY, size, size))
				unoccupied=false;
		}

		// if spot is available move/place object
		if (unoccupied==true){
			SPACE = new Rectangle2D.Double(upperLeftX, upperLeftY, size, size);
		}
		return unoccupied;
	}
	
	/**
	 * Still needs to be implemented. 
	 * Checks to see if the projectile has hit the object. If the projectile does 
	 * hit the object, return {@code true}. If the object is an enemy then it will 
	 * deduct the hit amount from the enemy life points.  
	 * 
	 * @param object Still needs to be implemented.  
	 * @return Still needs to be implemented. 
	 */
	public boolean hitObject(int object){
		
		return false;
	}
}
